<template>
    <div class="gantt-timeline">
        <div class="gantt-timeline-padding_block" :style="{ width: paddingWidth + 'px' }"></div>
        <template v-for="(day, index) in allDayBlocks">
            <div
                v-if="isRender(day)"
                :key="index"
                class="gantt-timeline-block"
                :style="{ width: getTimeScales(day).length * cellWidth + 'px' }"
            >
                <slot :day="day" :getTimeScales="getTimeScales">
                    <div class="gantt-timeline-scale" :style="heightStyle">
                        <div
                            v-for="(time, index) in getTimeScales(day)"
                            :key="index"
                            :style="{
                                width: `${cellWidth}px`
                            }"
                        >
                            <slot :times="{ day, time }">
                                {{ day.format('MM/DD') }}
                                <span v-if="!dayScale">{{ time.format('HH:mm') }}</span>
                            </slot>
                        </div>
                    </div>
                </slot>
            </div>
        </template>
    </div>
</template>

<script lang="ts" setup>
import dayjs, { type Dayjs } from 'dayjs';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
import isBetween from 'dayjs/plugin/isBetween';
import { isDayScale, MINUTE_OF_ONE_DAY, getstartOfTime } from '../utils/timeLineUtils.js';

dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter);
dayjs.extend(isBetween);

// const START_DAY = Symbol();
// const MIDDLE_DAY = Symbol();
// const END_DAY = Symbol();

const props = defineProps<{
    start: Dayjs;
    end: Dayjs;
    cellWidth: number;
    titleHeight: number;
    scale: number;
    getPositionOffset: (date: string) => number;
    eRenderTime?: Dayjs;
    sRenderTime?: Dayjs;
}>();

function isSameDay(one: Dayjs, two?: Dayjs) {
    return one.isSame(two, 'day');
}

function isSameOrBetween(start?: Dayjs, end?: Dayjs, mid?: Dayjs) {
    if (mid) return mid.isSameOrAfter(start) && mid.isSameOrBefore(end);
    return false;
}

const startRenderDay = computed(() => {
    return props.sRenderTime?.startOf('day');
});

const endRenderDay = computed(() => {
    return props.eRenderTime?.endOf('day');
});

// 虚拟DOM向左滚动的距离 用padding来做限制
const paddingWidth = computed(() => {
    const { scale, getPositionOffset } = props;
    const temp = allDayBlocks.value.find(day => {
        if (
            scale >= MINUTE_OF_ONE_DAY &&
            startRenderDay.value?.isBetween(day, day.add(scale / MINUTE_OF_ONE_DAY, 'day'))
        ) {
            return true;
        } else {
            return isSameDay(day, startRenderDay.value);
        }
    });
    if (!temp || temp === allDayBlocks.value[0]) {
        return 0;
    } else {
        return getPositionOffset(temp.toString());
    }
});

const dayScale = computed(() => isDayScale(props.scale));

/**
 * @: 全部日期
 * @param {*} computed
 * @return {*}
 */
const allDayBlocks = computed(() => {
    const temp = [];
    let { start, end, scale } = props;

    let tempStart = start.clone().startOf('day');
    let addNum = dayScale.value && scale > MINUTE_OF_ONE_DAY ? scale / MINUTE_OF_ONE_DAY : 1;
    while (tempStart.isSameOrBefore(end)) {
        temp.push(tempStart);
        tempStart = tempStart.add(addNum, 'day');
    }
    return temp;
});

const heightStyle = computed(() => {
    const { titleHeight } = props;
    return {
        height: titleHeight + 'px',
        'line-height': titleHeight + 'px'
    };
});

/**
 * @: 性能优化 判断传入时间是否渲染
 * @param {*} day
 * @return {*}
 */
const isRender = (day: Dayjs) => {
    const { scale } = props;
    if (
        scale >= MINUTE_OF_ONE_DAY &&
        startRenderDay.value?.isBetween(day, day.add(scale / MINUTE_OF_ONE_DAY, 'day'))
    )
        return true;

    return !!isSameOrBetween(startRenderDay.value, endRenderDay.value, day);
};

/**
 * 获取时间刻度数组
 *
 * @param {dayjs} date
 * @returns {[string]} 该data中所有需要渲染的数据
 */
const getTimeScales = (date: Dayjs) => {
    const totalblock = [];
    const { start, end, scale } = props;
    let a, b;

    if (isSameDay(date, start)) {
        //和start同一天
        a = getstartOfTime(start, scale);
        //start和end同一天特殊处理
        if (isSameDay(start, end)) {
            b = end;
        } else {
            b = start.endOf('day');
        }
    } else if (isSameDay(date, end)) {
        //和end 同一天
        a = end.startOf('day');
        b = end;
    } else {
        a = start.startOf('day');
        b = start.endOf('day');
    }

    while (!a.isAfter(b)) {
        totalblock.push(a);
        a = a.add(scale, 'minute');
    }

    return totalblock;
};
</script>
<style lang="scss" scoped>
.gantt-timeline {
    position: relative;
    display: flex;
    overflow: hidden;
    font-weight: 700;
    color: var(--q-text-color-secondary);
    text-align: center;
    background-color: var(--q-bg-color-secondary);
}

.gantt-timeline-padding_block {
    border-right: 1px solid #e6e6ea;
}

.gantt-timeline-day {
    overflow: hidden;
    font-weight: bold;
}

.gantt-timeline-scale {
    display: flex;
    cursor: grab;
    user-select: none;
    -webkit-touch-callout: none;
}

.gantt-timeline-scale > div {
    height: 100%;
    padding-left: 10px;
    font-size: 14px;
    text-align: left;
    border-right: 1px solid #e6e6ea;
}
</style>
